﻿
// KeyEventDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "KeyEvent.h"
#include "KeyEventDlg.h"
#include "afxdialogex.h"

#include <UserEnv.h>
#pragma comment(lib, "userenv.lib")

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#include "key_hook.h"
#pragma comment(lib, "KeyHook.lib")

// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CKeyEventDlg 对话框



CKeyEventDlg::CKeyEventDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_KEYEVENT_DIALOG, pParent)
	, target_path_(_T("")), backup_dir_(_T("")) {
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CKeyEventDlg::DoDataExchange(CDataExchange* pDX) {
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, target_path_);
	DDX_Text(pDX, IDC_EDIT2, backup_dir_);
	DDX_Control(pDX, IDC_HOTKEY1, hotkey_save_);
	DDX_Control(pDX, IDC_HOTKEY2, hotkey_load_);
}

BEGIN_MESSAGE_MAP(CKeyEventDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDOK, &CKeyEventDlg::OnBnClickedOk)
	ON_BN_CLICKED(IDCANCEL, &CKeyEventDlg::OnBnClickedCancel)
	ON_BN_CLICKED(IDC_BTN_FILE, &CKeyEventDlg::OnBnClickedBtnFile)
	ON_BN_CLICKED(IDC_BTN_DIR, &CKeyEventDlg::OnBnClickedBtnDir)
	ON_EN_CHANGE(IDC_EDIT1, &CKeyEventDlg::OnEnChangeEdit1)
	ON_EN_CHANGE(IDC_EDIT2, &CKeyEventDlg::OnEnChangeEdit2)
//	ON_WM_HOTKEY()
END_MESSAGE_MAP()


// CKeyEventDlg 消息处理程序

BOOL CKeyEventDlg::OnInitDialog() {
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr) {
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty()) {
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时，框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	// 从默认的配置文件中加载配置信息
	CString dir = GetDefaultConfigFile() + _T("\\Documents\\KeyEvent");
	config_path_ = dir + _T("\\config.ini");
	if (!PathFileExists(dir)) {
		CreateDirectory(dir, NULL);
		InitDefaultHotkey();
	} else if (PathFileExists(config_path_)) {
		LoadBackupConfig();
		LoadHotKeyConfig();
	}
	UpdateData(FALSE);

	if (StartKeyHook() != 0) {
		MessageBox(_T("Failed to start key hook!"));
	}
	return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE
}
void CKeyEventDlg::LoadBackupConfig() {
	TCHAR buff[1024] = { 0 };
	GetPrivateProfileString(_T("Event"), _T("TARGET"), NULL, buff, 1024, config_path_);
	target_path_ = buff;

	GetPrivateProfileString(_T("Event"), _T("BACKUP"), NULL, buff, 1024, config_path_);
	backup_dir_ = buff;

	auto suffix = PathFindFileName(target_path_);
	backup_path_ = backup_dir_ + _T('\\') + suffix;
	prev_backup_ = backup_path_ + _T(".prev");
}

void CKeyEventDlg::InitDefaultHotkey() {
	hotkey_save_.SetHotKey('S', HOTKEYF_CONTROL | HOTKEYF_ALT);
	hotkey_load_.SetHotKey('L', HOTKEYF_CONTROL | HOTKEYF_ALT);
}

void CKeyEventDlg::LoadHotKeyConfig() {
	TCHAR buff[128] = { 0 };
	if (GetPrivateProfileString(_T("Event"), _T("SAVE"), NULL, buff, 128, config_path_) > 0) {
		CString entry(buff);
		int pos = entry.Find(_T(','), 0);
		if (pos > 0) {
			auto key = StringToInt(entry.Left(pos));
			auto mask = StringToInt(entry.Right(entry.GetLength() - pos - 1));
			hotkey_save_.SetHotKey(key, mask);
			// SetupGlobalHotKey(kHotKeySaveAction, key, mask);

		} else {
			WritePrivateProfileString(_T("Event"), _T("SAVE"), _T("0,0"), config_path_);
			hotkey_save_.SetHotKey(0, 0);
		}
	}


	if (GetPrivateProfileString(_T("Event"), _T("LOAD"), NULL, buff, 128, config_path_) > 0) {
		CString entry(buff);
		int pos = entry.Find(_T(','), 0);
		if (pos > 0) {
			auto key = StringToInt(entry.Left(pos));
			auto mask = StringToInt(entry.Right(entry.GetLength() - pos - 1));
			hotkey_load_.SetHotKey(key, mask);
			// SetupGlobalHotKey(kHotKeyLoadAction, key, mask);

		} else {
			WritePrivateProfileString(_T("Event"), _T("LOAD"), _T("0,0"), config_path_);
			hotkey_load_.SetHotKey(0, 0);
		}
	}
}

int  CKeyEventDlg::StringToInt(const CString& s) {
	int n;
	try {
		n = _ttoi(s);
	} catch (...) {
		n = 0;
	}
	return n;
}
void CKeyEventDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else if (nID == SC_CLOSE) {
		StopKeyHook();
		CDialogEx::OnOK();
	} else {
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CKeyEventDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CKeyEventDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CKeyEventDlg::OnBnClickedOk() {

	WORD key = 0, mask = 0;
	hotkey_save_.GetHotKey(key, mask);

	CString name = hotkey_save_.GetHotKeyName();
	TRACE(_T("SAVE HOTKEY: [%s]\n"), name);

	CString info;
	info.Format(_T("%u,%u"), key, mask);
	WritePrivateProfileString(_T("Event"), _T("SAVE"), info, config_path_);

	SetupGlobalHotKey(kHotKeySaveAction, key, mask);
}


void CKeyEventDlg::OnBnClickedCancel() {
	WORD key = 0, mask = 0;
	hotkey_load_.GetHotKey(key, mask);

	CString name = hotkey_load_.GetHotKeyName();
	TRACE(_T("LOAD HOTKEY: [%s]\n"), name);

	CString info;
	info.Format(_T("%u,%u"), key, mask);
	WritePrivateProfileString(_T("Event"), _T("LOAD"), info, config_path_);

	SetupGlobalHotKey(kHotKeyLoadAction, key, mask);
}


void CKeyEventDlg::OnBnClickedBtnFile() {
	// TODO: 在此添加控件通知处理程序代码
	LPCTSTR szFilter = _T("SL2 File (*.sl2)|*.sl2|Text File (*.txt)|*.txt|Log File (*.log)|*.log|All Types (*.*)|*.*||");
	CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter, NULL);
	if (dlg.DoModal() == IDOK) {
		target_path_ = dlg.GetPathName();
		UpdateData(FALSE);

		WritePrivateProfileString(_T("Event"), _T("TARGET"), target_path_, config_path_);

		auto suffix = PathFindFileName(target_path_);
		backup_path_ = backup_dir_ + _T('\\') + suffix;
		prev_backup_ = backup_path_ + _T(".prev");
	}
}


void CKeyEventDlg::OnBnClickedBtnDir() {
	// TODO: 在此添加控件通知处理程序代码
	CFolderPickerDialog dlg;
	if (dlg.DoModal() == IDOK) {
		backup_dir_ = dlg.GetPathName();
		UpdateData(FALSE);

		WritePrivateProfileString(_T("Event"), _T("BACKUP"), backup_dir_, config_path_);

		auto suffix = PathFindFileName(target_path_);
		backup_path_ = backup_dir_ + _T('\\') + suffix;
		prev_backup_ = backup_path_ + _T(".prev");
	}
}


void CKeyEventDlg::OnEnChangeEdit1() {}

void CKeyEventDlg::OnEnChangeEdit2() {}

CString CKeyEventDlg::GetDefaultConfigFile() {
	HANDLE token = NULL;
	OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token);
	TCHAR buff[1024] = { 0 };
	DWORD len = 1024;
	GetUserProfileDirectory(token, buff, &len);
	TRACE(_T("USER HOME DIR: [%s]"), buff);
	return buff;
}

void CKeyEventDlg::SetupGlobalHotKey(int id, WORD key, WORD mask) {

	int count = 0;
	int vk_code[5] = { 0 };

	if (mask & HOTKEYF_CONTROL) {
		vk_code[count++] = VK_LCONTROL;
	}
	if (mask & HOTKEYF_ALT) {
		vk_code[count++] = VK_LMENU;
	}
	if (mask & HOTKEYF_SHIFT) {
		vk_code[count++] = VK_LSHIFT;
	}

	vk_code[count++] = key;

	intptr_t user = reinterpret_cast<intptr_t>(this);

	RegisterGlobalHotKey(id, vk_code, count, user, [](int nHotKeyId, intptr_t user) {
			auto pthis = reinterpret_cast<CKeyEventDlg*>(user);
			pthis->OnGlobalHotKey(nHotKeyId);
		}
	);
}
/*
void CKeyEventDlg::OnHotKey(UINT nHotKeyId, UINT nKey1, UINT nKey2) {

	TRACE(_T("HOTKEY EXECUTION : ID -> %d , KEY -> %d , KEY -> %d\n"), nHotKeyId, nKey1, nKey2);

	if (nHotKeyId == kHotKeyLoadAction) {

		// 恢复操作，从备份目录加载文件
		CopyFile(backup_path_, target_path_, false);

	} else if (nHotKeyId == kHotKeySaveAction) {

		// 备份操作，拷贝目标文件到备份目录
		CopyFile(backup_path_, prev_backup_, false);
		CopyFile(target_path_, backup_path_, false);
	}
	CDialogEx::OnHotKey(nHotKeyId, nKey1, nKey2);
}
*/

void CKeyEventDlg::OnGlobalHotKey(int nHotKeyId) {

	TRACE(_T("HOTKEY EXECUTION : ID -> %d\n"), nHotKeyId);

	if (nHotKeyId == kHotKeyLoadAction) {

		// 恢复操作，从备份目录加载文件
		CopyFile(backup_path_, target_path_, false);

	} else if (nHotKeyId == kHotKeySaveAction) {

		// 备份操作，拷贝目标文件到备份目录
		CopyFile(backup_path_, prev_backup_, false);
		CopyFile(target_path_, backup_path_, false);
	}
}
